/*
 * Copyright (C) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "render/lottie_render.h"
#include "canvas.h"
#include "common/common.h"
#include "common/number_constant.h"
#include "lottie_render_gpu.h"
#include "native_drawing/drawing_gpu_context.h"
#include "native_drawing/drawing_surface.h"
#include "node/lottie_manager_map.h"
#include "task_pool/task_pool.h"
#include <cstdint>
#include <deviceinfo.h>
#include <native_buffer/native_buffer.h>

namespace LottieTurbo {

LottieRender::LottieRender()
{
    lottieRenderNativeWindow_ = std::make_shared<LottieRenderNativeWindow>();
    lottieRenderLayer_ = std::make_shared<LottieRenderLayer>();
    canvas_ = std::make_shared<drawing::Canvas>();
}

LottieRender::~LottieRender()
{
    if (gpuContext_ != nullptr) {
        auto func = [this](OH_Drawing_GpuContext *gpuContext) {
            if (gpuContext != nullptr) {
                LottieGpuFun::GetInstance().RenderGpuDestroy(gpuContext);
                gpuContext = nullptr;
            }
        };
        TaskPoolLocal::GetInstance().PushTask(func, gpuContext_);
    }
    for (auto &it : imageCache_) {
        free(it.second->bitmap.GetPixels());
    }
    imageCache_.clear();
}

bool LottieRender::ImageCacheState(int32_t width, int32_t height, float frame)
{
    auto frameString = std::to_string(std::round(frame));
    auto it = imageCache_.find(frameString);
    if (it != imageCache_.end() && it->second && it->second->width == width && it->second->height == height) {
        return true;
    }
    return false;
}

bool LottieRender::RenderImageCache(const LOTLayerNode *rootNode, int32_t width, int32_t height, float frame)
{
    auto frameString = std::to_string(std::round(frame));
    auto it = imageCache_.find(frameString);
    OH_Drawing_Image_Info imageinfo = {static_cast<int32_t>(width), static_cast<int32_t>(height),
                                       COLOR_FORMAT_RGBA_8888, ALPHA_FORMAT_PREMUL};
    if (it != imageCache_.end() && it->second && it->second->width == width && it->second->height == height) {
        canvas_->DrawBitmap(it->second->bitmap.get(), 0, 0);
        DRAWING_LOGD("LottieRender:: DrawBitmap %{public}s", frameString.c_str());
    } else {
        drawing::Bitmap tempBitmap;
        lottieRenderLayer_->RenderLayer(canvas_, rootNode);
        void *bitmap = malloc(width * height * RGBANUM);
        bool ret = canvas_->ReadPixels(&imageinfo, bitmap, width * RGBANUM, 0, 0);
        if (!ret) {
            free(bitmap);
            return false;
        }
        tempBitmap = tempBitmap.CreateFromPixels(&imageinfo, bitmap, width * RGBANUM);
        if (it != imageCache_.end()) {
            free(it->second->bitmap.GetPixels());
            imageCache_[frameString]->width = width;
            imageCache_[frameString]->height = height;
            imageCache_[frameString]->bitmap = std::move(tempBitmap);
        } else {
            imageCache_.emplace(frameString, std::make_unique<ImageCache>(ImageCache{
                                                 width, height,
                                                 std::move(tempBitmap) // 显式移动资源
                                             }));
        }
    }
    return true;
}

void LottieRender::RenderTree(const std::string xcomponentId, const LOTLayerNode *rootNode,
                              CalculatePosition *calculatePosition, float frame)
{
    if (calculatePosition->width <= 0 || calculatePosition->height <= 0) {
        DRAWING_LOGE("LottieRender::RenderTree width or height is 0");
        return;
    }
    auto window = LottieManagerMap::getInstance().GetWindowById(xcomponentId);
    if (!window) {
        DRAWING_LOGE("Failed to get window for xcomponentId: %{public}s", xcomponentId.c_str());
        return;
    }

    if (gpuContext_ == nullptr) {
        gpuContext_ = LottieGpuFun::GetInstance().RenderGpuCreate();
    }
    int32_t width = 0;
    int32_t height = 0;
    lottieRenderLayer_->SetSize(calculatePosition->width, calculatePosition->height);
    LottieManagerMap::getInstance().GetRectById(xcomponentId, width, height);
    if (width == 0 || height == 0) {
        return;
    }
    OH_Drawing_Image_Info imageinfo = {static_cast<int32_t>(width), static_cast<int32_t>(height),
                                       COLOR_FORMAT_RGBA_8888, ALPHA_FORMAT_PREMUL};
    surface_ = LottieGpuFun::GetInstance().RenderGpuSurface(gpuContext_, imageinfo, window);
    canvas_ = std::make_shared<drawing::Canvas>(OH_Drawing_SurfaceGetCanvas(surface_));
    this->HandleCanvas(canvas_, calculatePosition);
    auto handle = LottieManagerMap::getInstance().GetHandleById(xcomponentId);
    if (!handle || !handle->GetLottieConfig()) {
        return;
    }
    auto useImageCache = handle->GetLottieConfig()->GetImageCache();
    if (rootNode == nullptr && useImageCache == false) {
        DRAWING_LOGE("LottieRender::RenderTree rootNode is nullptr");
        return;
    }
    if (useImageCache) {
        RenderImageCache(rootNode, width, height, frame);
    } else {
        lottieRenderLayer_->RenderLayer(canvas_, rootNode);
    }
    lottieRenderLayer_->PrintRenderInfo();
    LottieGpuFun::GetInstance().RenderGpuFlash(surface_);
    OH_Drawing_SurfaceDestroy(surface_);
}

void LottieRender::GpuRenderClear(const std::string xcomponentId)
{
    auto window = LottieManagerMap::getInstance().GetWindowById(xcomponentId);
    if (!window) {
        DRAWING_LOGE("Failed to get window for xcomponentId: %{public}s", xcomponentId.c_str());
        return;
    }
    if (gpuContext_ == nullptr) {
        gpuContext_ = LottieGpuFun::GetInstance().RenderGpuCreate();
    }
    int32_t width = 0;
    int32_t height = 0;
    LottieManagerMap::getInstance().GetRectById(xcomponentId, width, height);
    if (width == 0 || height == 0) {
        return;
    }
    OH_Drawing_Image_Info imageinfo = {static_cast<int32_t>(width), static_cast<int32_t>(height),
                                       COLOR_FORMAT_RGBA_8888, ALPHA_FORMAT_PREMUL};
    surface_ = LottieGpuFun::GetInstance().RenderGpuSurface(gpuContext_, imageinfo, window);
    canvas_ = std::make_shared<drawing::Canvas>(OH_Drawing_SurfaceGetCanvas(surface_));
    canvas_->Clear(0);
    LottieGpuFun::GetInstance().RenderGpuFlash(surface_);
    OH_Drawing_SurfaceDestroy(surface_);
}

bool LottieRender::RenderToImage(const LOTLayerNode *rootNode, float width, float height, void *bitmap)
{
    if (rootNode == nullptr || width <= 0 || height <= 0) {
        DRAWING_LOGE("LottieRender::RenderToImage, Input Error.");
        return false;
    }
    OH_Drawing_Image_Info imageinfo = {static_cast<int32_t>(width), static_cast<int32_t>(height),
                                       COLOR_FORMAT_RGBA_8888, ALPHA_FORMAT_PREMUL};

    OH_Drawing_GpuContext *offScreenGpuContext_ = LottieGpuFun::GetInstance().RenderGpuCreate();
    OH_Drawing_Surface *offScreenSurface_ =
        LottieGpuFun::GetInstance().RenderGpuSurfaceFromContext(offScreenGpuContext_, false, imageinfo);
    if (offScreenSurface_ == nullptr) {
        DRAWING_LOGE("LottieRender::RenderToImage, Surface Null");
        return false;
    }
    auto offScreenCanvas_ = std::make_shared<drawing::Canvas>(OH_Drawing_SurfaceGetCanvas(offScreenSurface_));
    if (offScreenCanvas_ == nullptr) {
        DRAWING_LOGE("LottieRender::RenderToImage, Canvas Null");
        return false;
    }
    lottieRenderLayer_->RenderLayer(offScreenCanvas_, rootNode);
    bool ret = offScreenCanvas_->ReadPixels(&imageinfo, bitmap, width * RGBANUM, 0, 0);
    if (offScreenSurface_ != nullptr) {
        OH_Drawing_SurfaceDestroy(offScreenSurface_);
        offScreenSurface_ = nullptr;
    }
    if (offScreenGpuContext_ != nullptr) {
        LottieGpuFun::GetInstance().RenderGpuDestroy(offScreenGpuContext_);
        offScreenGpuContext_ = nullptr;
    }
    return ret;
}

void LottieRender::HandleCanvas(std::shared_ptr<drawing::Canvas> canvas, CalculatePosition *calculatePosition)
{
    if (canvas == nullptr) {
        return;
    }
    // 使用透明清除画布内容
    canvas->Clear(drawing::Color::SetArgb(0, 0, 0, 0));
    // 应用偏移
    if (calculatePosition->x != 0 || calculatePosition->y != 0) {
        canvas->Translate(static_cast<float>(calculatePosition->x), static_cast<float>(calculatePosition->y));
    }
    if (calculatePosition->crop) {
        drawing::Rect rect(0, 0, calculatePosition->width, calculatePosition->height);
        canvas_->ClipRect(rect.get(), INTERSECT, false);
    }
}

void LottieRender::CpuRenderTree(const std::string xcomponentId, const LOTLayerNode *rootNode,
                                 CalculatePosition *calculatePosition)
{
    if (!rootNode || calculatePosition->width <= 0 || calculatePosition->height <= 0) {
        DRAWING_LOGE("LottieRender::cpuRenderTree Invalid parameters for RenderTree");
        return;
    }
    auto window = LottieManagerMap::getInstance().GetWindowById(xcomponentId);
    if (!window) {
        DRAWING_LOGE("LottieRender::cpuRenderTree Failed to get window for xcomponentId: %s", xcomponentId.c_str());
        return;
    }
    auto nativeWindow = static_cast<OHNativeWindow *>(window);
    // 准备渲染环境
    if (!(canvas_ = lottieRenderNativeWindow_->PrepareRender(nativeWindow))) {
        DRAWING_LOGE("LottieRender::cpuRenderTree Failed to prepare render environment");
        lottieRenderNativeWindow_->MunMap();
        return;
    }
    this->HandleCanvas(canvas_, calculatePosition);
    lottieRenderLayer_->SetSize(calculatePosition->width, calculatePosition->height);
    // 处理根层节点
    lottieRenderLayer_->RenderLayer(canvas_, rootNode);
    lottieRenderNativeWindow_->DisplayResult(nativeWindow);
}

void LottieRender::CpuRenderClear(const std::string xcomponentId)
{
    auto window = LottieManagerMap::getInstance().GetWindowById(xcomponentId);
    if (!window) {
        DRAWING_LOGE("LottieRender::cpuRenderTree Failed to get window for xcomponentId: %s", xcomponentId.c_str());
        return;
    }
    auto nativeWindow = static_cast<OHNativeWindow *>(window);
    // 准备渲染环境
    if (!(canvas_ = lottieRenderNativeWindow_->PrepareRender(nativeWindow))) {
        DRAWING_LOGE("LottieRender::cpuRenderTree Failed to prepare render environment");
        lottieRenderNativeWindow_->MunMap();
        return;
    }
    canvas_->Clear(0);
    lottieRenderNativeWindow_->DisplayResult(nativeWindow);
}

void LottieRender::RenderClear(const std::string xcomponentId)
{
    if (OH_GetSdkApiVersion() >= NUM_GPUAPI) {
        GpuRenderClear(xcomponentId);
    } else {
        CpuRenderClear(xcomponentId);
    }
}

void LottieRender::Render(const std::string xcomponentId, const LOTLayerNode *rootNode,
                          CalculatePosition *calculatePosition, float frame)
{
    if (OH_GetSdkApiVersion() >= NUM_GPUAPI) {
        RenderTree(xcomponentId, rootNode, calculatePosition, frame);
    } else {
        CpuRenderTree(xcomponentId, rootNode, calculatePosition);
    }
}
} // namespace LottieTurbo